﻿#include "businessmsghandler.h"

#include "businessnetwork.h"
#include "AppConfig/appconfig.h"

#include "protocol/protocolConverter.h"

#include "CipherUtils/cipherutils.h"
#include <SyncManager/syncmanager.h>
#include "businessuser.h"
#include <UserManager/usermanager.h>

#include "businessviewmodel.h"
#include <iostream>
#include <Debug/DEBUG.hpp>
#include "businessfile.h"
#include "businessgroup.h"
#include "businesssql.h"


void BusinessMsgHandler::init()
{

    // register call back function

    msgRouter.registerCallback("7|2", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvServerConfig(protocol);
    });

    msgRouter.registerCallback("4|3", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvReplyOfApply(protocol);
    });
    msgRouter.registerCallback("4|4", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvReplyOfApply(protocol);
    });
    msgRouter.registerCallback("6|2", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessUser::getInstance().initUserList(protocol);
    });
    msgRouter.registerCallback("1|1", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvUserMessage(protocol);
    });
    msgRouter.registerCallback("1|4", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvOfflineUserMessage(protocol);
    });
    msgRouter.registerCallback("4|2", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvUniqueID(protocol);
    });
    msgRouter.registerCallback("5|2", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvConfirmOfFileBlock(protocol);
    });
    msgRouter.registerCallback("6|3", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvAllFile(protocol);
    });
    msgRouter.registerCallback("3|1",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessFile::getInstance().recvUploadFileRequest(protocol);
    });
    msgRouter.registerCallback("3|3",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessFile::getInstance().recvFileBlock(protocol);
    });
    msgRouter.registerCallback("3|4",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessFile::getInstance().recvEndFileTransmission(protocol);
    });
    msgRouter.registerCallback("6|4",[](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessViewModel::getInstance().updateStatusbarText(protocol);
    });
    msgRouter.registerCallback("6|5", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().updateUserList(protocol);
    });
    msgRouter.registerCallback("2|11", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessUser::getInstance().addUser(protocol);
    });
    msgRouter.registerCallback("6|6", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().updateGroupList(protocol);
    });
    msgRouter.registerCallback("1|2", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessMsgHandler::getInstance().RecvGroupMessage(protocol);
    });
    msgRouter.registerCallback("6|7", [](std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel){
        BusinessGroup::getInstance().updateGroupRequestList(protocol);
    });

}

void BusinessMsgHandler::handleMsg(std::shared_ptr<Protocol> protocol, const hv::SocketChannelPtr& channel)
{
    AppConfig& config = AppConfig::getInstance();

    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());

    converter.parseData((unsigned char*)protocol->data());

    int type = std::get<int64_t>(converter.getHeaderField(0));
    int subType = std::get<int64_t>(converter.getHeaderField(1));
    if (type != 7 || subType != 2) {
        // decrypt message
        auto mode = config.runtimeValue("TransmissionMode");
        if (mode == "2") {
            // get user aesKey and iv

            std::string AESKey = config.runtimeValue("RemoteAESKey");
            std::string IV = config.runtimeValue("RemoteAESIV");

            CipherUtils& utils = CipherUtils::getInstance();

            std::string cipherText;
            cipherText.resize(converter.getParsedDataSize());
            converter.copyParsedData(cipherText.data());

            auto res = utils.AES_Decrypt((unsigned char*)cipherText.data(), cipherText.size(),
                                         (unsigned char*)AESKey.data(),
                                         (unsigned char*)IV.data());

            ProtocolBuilder builder;
            builder.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
            builder.updateProtocolData(protocol, res.first.get(), res.second);

            DEBUG_INFO("decrypted the message");
        }
    }


    std::string commandStr = std::to_string(type) + "|" + std::to_string(subType);
    DEBUG_INFO("receiver commandStr" + commandStr);
    // exec target function
    msgRouter[commandStr](protocol, channel);
}

void BusinessMsgHandler::RecvServerConfig(std::shared_ptr<Protocol> protocol)
{
    /**
     * 1. ECC_public
     * 2. TransmissionMode
     */

    nlohmann::json message = getJsonOfMessage(protocol);

    // compute the AESKey and AESIV

    std::string ECC_public2 = message["ECC_public"];

    CipherUtils& utils = CipherUtils::getInstance();

    AppConfig& config = AppConfig::getInstance();

    auto ECDHKey = utils.ECDH_computeKey(config.globalValue("ECC_private"), ECC_public2);

    auto AESandIV = utils.AES_generateFromECDHSharedKey(ECDHKey.first, ECDHKey.second);

    std::string AESkey, AESIV;

    AESkey.resize(256/8);
    AESIV.resize(128/8);

    memcpy(AESkey.data(), AESandIV[0].get(), 256/8);
    memcpy(AESIV.data(), AESandIV[1].get(), 128/8);

    config.setRuntimeValue("RemoteAESKey", AESkey);
    config.setRuntimeValue("RemoteAESIV", AESIV);

    config.setRuntimeValue("TransmissionMode", message["TransmissionMode"]);

}

void BusinessMsgHandler::RecvReplyOfApply(std::shared_ptr<Protocol> protocol)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char *)protocol->data());
    uint64_t id3 = std::get<int64_t>(converter.getHeaderField(4));
    int subType = std::get<int64_t>(converter.getHeaderField(1));

    SyncManager& manager = SyncManager::getInstance();

    if (subType == 3) {
        // accept the apply
        manager.releaseSyncPoint(id3, true);
    } else {
        // reject the apply
        manager.releaseSyncPoint(id3, false);
    }
}

void BusinessMsgHandler::RecvUserMessage(std::shared_ptr<Protocol> protocol)
{
    // get source user;
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    auto sourceUser = std::get<int64_t>(converter.getHeaderField(2));
    auto targetUser = std::get<int64_t>(converter.getHeaderField(3));

    UserManager& manager = UserManager::getInstance();
    // get user account and message content
    User& user = BusinessUser::getInstance().getTargetUser(sourceUser);
    nlohmann::json data = getJsonOfMessage(protocol);
    // if it is self
    if (sourceUser == std::stoi(AppConfig::getInstance().runtimeValue("UserID"))) {
        User& toUser = BusinessUser::getInstance().getTargetUser(targetUser);
        BusinessViewModel::getInstance().addMessageFromSelf(toUser, data["Message"], data["MessageType"], data["Time"]);
    } else {
        // add message to target user
        BusinessViewModel::getInstance().addMessageFromUser(user , data["Message"], data["MessageType"], data["Time"]);
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    BusinessSQL::getInstance().saveUserMessage(sourceUser, targetUser, data);
}

void BusinessMsgHandler::RecvGroupMessage(std::shared_ptr<Protocol> protocol)
{
    // get source user;
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    auto sourceUser = std::get<int64_t>(converter.getHeaderField(2));

    // get user account and message content
    User& user = BusinessUser::getInstance().getTargetUser(sourceUser);

    nlohmann::json messageJson = getJsonOfMessage(protocol);

    uint64_t targetGroup = std::get<int64_t>(converter.getHeaderField(3));

    // add message to target user
    const std::string& message = messageJson["Message"];
    int messageType = messageJson["MessageType"];
    const std::string timeStr = messageJson["Time"];
    BusinessViewModel::getInstance().addMessageFromGroup(user, targetGroup, message, messageType, timeStr);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    BusinessSQL::getInstance().saveGroupMessage(sourceUser, targetGroup, messageJson);

}

void BusinessMsgHandler::RecvOfflineUserMessage(std::shared_ptr<Protocol> protocol)
{
    // get source user;
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());

    auto sourceUser = std::get<int64_t>(converter.getHeaderField(4));

    UserManager& manager = UserManager::getInstance();
    // get user account and message content
    User& user = manager.getTargetUser(sourceUser);
    nlohmann::json messageJson = getJsonOfMessage(protocol);

    std::string time = messageJson["Time"];
    std::string message = messageJson["Message"];
    int messageType = messageJson["MessageType"];
    // add message to target user
    BusinessViewModel::getInstance().addMessageFromUser(user, message, messageType, time);

    uint64_t selfID = std::stoi(AppConfig::getInstance().runtimeValue("UserID"));
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    BusinessSQL::getInstance().saveUserMessage(sourceUser, selfID, messageJson);
}

void BusinessMsgHandler::RecvUniqueID(std::shared_ptr<Protocol> protocol)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char *)protocol->data());

    uint64_t uniqueID = std::get<int64_t>(converter.getHeaderField(2));
    DEBUG_INFO("BusinessMsgHandler::RecvUniqueID : " + std::to_string(uniqueID));
    SyncPoint syncPoint = std::get<int64_t>(converter.getHeaderField(4));
    SyncManager& manager = SyncManager::getInstance();
    manager.releaseSyncPoint(syncPoint, uniqueID);
}

void BusinessMsgHandler::RecvConfirmOfFileBlock(std::shared_ptr<Protocol> protocol)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char *)protocol->data());

    SyncPoint syncPoint = std::get<int64_t>(converter.getHeaderField(4));
    SyncManager& manager = SyncManager::getInstance();
    manager.releaseSyncPoint(syncPoint, true);
}

void BusinessMsgHandler::RecvAllFile(std::shared_ptr<Protocol> protocol)
{


    nlohmann::json fileInfo = getJsonOfMessage(protocol);
    BusinessViewModel::getInstance().updateFileList(fileInfo);
}

void BusinessMsgHandler::updateUserList(std::shared_ptr<Protocol> protocol)
{
    nlohmann::json users = BusinessMsgHandler::getJsonOfMessage(protocol);
    nlohmann::json friendList = users["friendMessage"];
    nlohmann::json pendingFriendList = users["pendingMessage"];

    BusinessViewModel::getInstance().updateUserList(friendList);
    BusinessViewModel::getInstance().updateUserList(pendingFriendList);
}

BusinessMsgHandler::BusinessMsgHandler() {}

nlohmann::json BusinessMsgHandler::getJsonOfMessage(std::shared_ptr<Protocol> protocol)
{
    ProtocolConverter converter;
    converter.setProtocolOption(BusinessNetWork::getInstance().getProtocolOption());
    converter.parseData((unsigned char*)protocol->data());
    std::string jsonStr;
    jsonStr.resize(converter.getParsedDataSize(), '\0');
    converter.copyParsedData(jsonStr.data());
    return nlohmann::json::parse(jsonStr);
}
